Skip to content

feat: mcp apps for posthog's mcp#2459

Merged
skoob13 merged 3 commits into
mainfrom
feat/mcp-apps-posthog-mcp
Jun 4, 2026
Merged

feat: mcp apps for posthog's mcp#2459
skoob13 merged 3 commits into
mainfrom
feat/mcp-apps-posthog-mcp

Conversation

@skoob13
Copy link
Copy Markdown
Contributor

@skoob13 skoob13 commented Jun 2, 2026

Problem

We'd like to render MCP apps for PostHog's MCP. However, the CLI MCP mode is not compliant with the MCP spec, so we needed to alter the MCP apps renderer.

Changes

Standard MCP Apps bind a tool to its UI app upfront via registration metadata. PostHog instead exposes all UI apps through one generic exec tool and puts the ui:// resource URI on each tool-call response. This branch adds that per-call resolution path:

  • Shared typesPOSTHOG_EXEC_TOOL_KEY constants + resolveResultResourceUri() (reads the URI from a result's _meta, modern key with legacy fallback) + getUiResourceByUriInput schema.
  • Main service — new getUiResourceByUri() (+ shared fetchUiResourceByUri/doFetchUiResource with caching); special-cases posthog/exec during discovery; treats "No server config" boot-race failures as transient.
  • Router — new getUiResourceByUri procedure.
  • RendererMcpAppHost/McpToolBlock resolve the URI from toolCall.rawOutput (persisted → survives restarts), fetch by URI, dedup result delivery per toolCallId, and re-fetch on onDiscoveryComplete.
  • Auth — adds x-posthog-mcp-consumer: posthog-code header.

How did you test this?

Tests added across all three layers (service.test.ts, mcp-apps.test.ts, auth-adapter.test.ts).

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

@skoob13 skoob13 requested a review from a team June 2, 2026 10:03
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 2, 2026

Comments Outside Diff (1)

  1. apps/code/src/main/services/mcp-apps/service.test.ts, line 79-85 (link)

    P1 Test asserts resolves.toBeNull() but the implementation now rejects

    doFetchUiResource re-throws connection failures instead of returning null. When there is no server config, getOrCreateConnection throws Error: No server config for: posthog, which is caught and re-thrown by doFetchUiResource. Neither fetchUiResourceByUri nor getUiResourceByUri has a catch block, so the promise rejects entirely. expect(...).resolves.toBeNull() asserts a resolved promise — this test will fail with a rejection error.

    The test comment even describes the new intent ("boot-race failures as transient"), but the assertion is the opposite of that intent. It should be rejects.toThrow(...) to match the rethrow path, or the test needs to set up a server config so the connection succeeds.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/code/src/main/services/mcp-apps/service.test.ts
    Line: 79-85
    
    Comment:
    **Test asserts `resolves.toBeNull()` but the implementation now rejects**
    
    `doFetchUiResource` re-throws connection failures instead of returning `null`. When there is no server config, `getOrCreateConnection` throws `Error: No server config for: posthog`, which is caught and re-thrown by `doFetchUiResource`. Neither `fetchUiResourceByUri` nor `getUiResourceByUri` has a `catch` block, so the promise rejects entirely. `expect(...).resolves.toBeNull()` asserts a *resolved* promise — this test will fail with a rejection error.
    
    The test comment even describes the new intent ("boot-race failures as transient"), but the assertion is the opposite of that intent. It should be `rejects.toThrow(...)` to match the rethrow path, or the test needs to set up a server config so the connection succeeds.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/code/src/main/services/mcp-apps/service.test.ts:79-85
**Test asserts `resolves.toBeNull()` but the implementation now rejects**

`doFetchUiResource` re-throws connection failures instead of returning `null`. When there is no server config, `getOrCreateConnection` throws `Error: No server config for: posthog`, which is caught and re-thrown by `doFetchUiResource`. Neither `fetchUiResourceByUri` nor `getUiResourceByUri` has a `catch` block, so the promise rejects entirely. `expect(...).resolves.toBeNull()` asserts a *resolved* promise — this test will fail with a rejection error.

The test comment even describes the new intent ("boot-race failures as transient"), but the assertion is the opposite of that intent. It should be `rejects.toThrow(...)` to match the rethrow path, or the test needs to set up a server config so the connection succeeds.

### Issue 2 of 2
apps/code/src/shared/types/mcp-apps.test.ts:1-48
**Separate `it` blocks for one function — parameterised test preferred**

`resolveResultResourceUri` is called with five different inputs across separate `it` blocks, each testing a different input variant. Per the team's coding standard, this is the canonical case for `it.each` — a single parameterised test is easier to extend, makes the coverage matrix obvious at a glance, and avoids duplicating the `expect(resolveResultResourceUri(...))` call. The test that covers `undefined` already batches multiple cases on one assertion; the others could follow the same pattern (or consolidate into a table).

Reviews (1): Last reviewed commit: "wip" | Re-trigger Greptile

Comment thread apps/code/src/shared/types/mcp-apps.test.ts
Copy link
Copy Markdown
Contributor

@k11kirky k11kirky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another nit:

HOST_INFO in useAppBridge.ts:65 is still { name: "Twig", ... }, even though this PR renamed the client identity to posthog-code in both service.ts:171 and createConnection.

);

useEffect(() => {
log.info("render state", {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should probably remove this

skoob13 and others added 3 commits June 4, 2026 11:25
- Rename MCP host identity from "Twig" to "posthog-code" in useAppBridge,
  matching the client identity already used in the main service.
- Remove the debug "render state" useEffect from McpToolBlock.
- Fix the getUiResourceByUri test to expect a rejection: a missing server
  config rethrows (transient boot-race), it does not resolve to null.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@skoob13 skoob13 force-pushed the feat/mcp-apps-posthog-mcp branch from ac8493a to e71c2b7 Compare June 4, 2026 09:32
@skoob13 skoob13 enabled auto-merge (squash) June 4, 2026 09:35
@skoob13 skoob13 merged commit cb0e8b4 into main Jun 4, 2026
18 checks passed
@skoob13 skoob13 deleted the feat/mcp-apps-posthog-mcp branch June 4, 2026 09:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants